// iap.cpp
//
// iap file delegate (our own file format)

#include "delegates/iap.h"
#include <stdio.h>

namespace simple{
    //
    // IAP
    //
    // IAP storage delegate


    IAP* IAP::_sOnly = 0;

    IAP& IAP::gOnly()
    {
        if (!_sOnly) _sOnly = new IAP ();
        return *_sOnly;
    }

    IAP::IAP ()  {}
    IAP::~IAP () {}

    DelegateInfo IAP::info (const std::string& filename)
    {
        DelegateInfo info;
        info.isDelegate    = false;
        info.boundary      = Rect ();
        info.bytesPerPixel = 0;
        info.planes        = 0;

        // Open the file
        FILE *infile;
        if ((infile = fopen(filename.c_str(), "rb")) == 0) {
            return info;
        }

        // Read the header
        IAPFileHeader header;
        size_t read = fread (&header, sizeof (header), 1, infile);
        if (read != 1)
            return info;
        if (strncmp (header.signature, "IAP     ", 8) != 0)
            return info;

        // Only support a single image
        if (header.nImages != 1)
            return info;

        // Read the image header
        IAPImageHeader imageHeader;
        read = fread (&imageHeader, sizeof (imageHeader), 1, infile);
        if (read != 1)
            return info;

        info.isDelegate = true;
        info.boundary = Rect (imageHeader.x0, imageHeader.y0, imageHeader.width, imageHeader.height);
        info.bytesPerPixel = imageHeader.channels * imageHeader.depth/8;
        info.planes = imageHeader.channels;

        return info;
    }



    Image<Pel8> IAP::readPel8 (const std::string& filename)
    {
        Image<Pel8> dst;

        // Open the file
        FILE *infile;
        if ((infile = fopen(filename.c_str(), "rb")) == 0) {
            return dst;
        }

        // Read the header
        IAPFileHeader header;
        size_t read = fread (&header, sizeof (header), 1, infile);
        if (read != 1)
            return dst;
        if (strncmp (header.signature, "IAP     ", 8) != 0)
            return dst;

        // Only support a single image
        if (header.nImages != 1)
            return dst;

        // Read the image header
        IAPImageHeader imageHeader;
        read = fread (&imageHeader, sizeof (imageHeader), 1, infile);
        if (read != 1)
            return dst;

        if (imageHeader.type == eRGB)
            dst = _readRGB (infile, imageHeader);
        else if (imageHeader.type == eMono)
            dst = _readPel8 (infile, imageHeader);
        else
            return dst; // Unknown file format

        fclose (infile);
        return dst;
    }



    Image<RGB> IAP::readRGB (const std::string& filename)
    {
        Image<RGB> dst;

        // Open the file
        FILE *infile;
        if ((infile = fopen(filename.c_str(), "rb")) == 0) {
            return dst;
        }

        // Read the header
        IAPFileHeader header;
        size_t read = fread (&header, sizeof (header), 1, infile);
        if (read != 1)
            return dst;
        if (strncmp (header.signature, "IAP     ", 8) != 0)
            return dst;

        // Only support a single image
        if (header.nImages != 1)
            return dst;

        // Read the image header
        IAPImageHeader imageHeader;
        read = fread (&imageHeader, sizeof (imageHeader), 1, infile);
        if (read != 1)
            return dst;

        if (imageHeader.type == eRGB)
            dst = _readRGB (infile, imageHeader);
        else if (imageHeader.type == eMono)
            dst = _readPel8 (infile, imageHeader);
        else
            return dst; // Unknown file format

        fclose (infile);
        return dst;
    }


    Image<RGB> IAP::_readRGB (FILE* fp, const IAPImageHeader& imageHeader)
    {
        // Construct our destination image
        Rect boundary (imageHeader.x0, imageHeader.y0,
                                         imageHeader.width, imageHeader.height);
        Image<RGB> dst (boundary, RectImageStorage::eQuadWordAlign);

        // Process each line
        size_t read;
        long bytesPerRow = imageHeader.width * imageHeader.channels * imageHeader.depth/8;
        for (long y=0; y<imageHeader.height; y++) {
            read = fread (dst.rowAddress (y+imageHeader.y0), bytesPerRow, 1, fp);
            if (read != 1) {
                // We didn't read the information
                dst = dst.sNull;
                break;
            }
        }
        return dst;
    }

    Image<Pel8> IAP::_readPel8 (FILE* fp, const IAPImageHeader& imageHeader)
    {
        // Construct our destination image
        Rect boundary (imageHeader.x0, imageHeader.y0,
                                         imageHeader.width, imageHeader.height);
        Image<Pel8> dst (boundary, RectImageStorage::eQuadWordAlign);

        // Process each line
        size_t read;
        long bytesPerRow = imageHeader.width * imageHeader.channels * imageHeader.depth/8;
        for (long y=0; y<imageHeader.height; y++) {
            read = fread (dst.rowAddress (y+imageHeader.y0), bytesPerRow, 1, fp);
            if (read != 1) {
                // We didn't read the information
                dst = dst.sNull;
                break;
            }
        }
        return dst;
    }



    bool IAP::write (const std::string& filename,
                     const RectImageStorage& pixels, 
                     const DelegateParams& /*params*/)
    {
        IAPFileHeader header;
        strncpy (header.signature, "IAP     ", 8);
        header.version = 0;
        header.nImages = 1;

        // Assumes RGB
        IAPImageHeader imageHeader;
        imageHeader.x0       = pixels.x0 ();
        imageHeader.y0       = pixels.y0 ();
        imageHeader.width    = pixels.width ();
        imageHeader.height   = pixels.height ();
        imageHeader.format   = eNone;
        imageHeader.channels = 3;
        imageHeader.type     = eRGB;
        imageHeader.depth    = 8;


        if (pixels.bytesPerPixel() == 3) {
            // RGB
            imageHeader.channels = 3;
            imageHeader.type     = eRGB;
            imageHeader.depth    = 8;
        }
        else if (pixels.bytesPerPixel() == 1) {
            // Byte
            imageHeader.channels = 1;
            imageHeader.type     = eMono;
            imageHeader.depth    = 8;
        }
        else {
            // Other pixel format. Convert to RGB...
        }

        FILE *outfile;
        if ((outfile = fopen(filename.c_str(), "wb")) == 0)
            return false;

        // Write the headers
        size_t bytes;
        bytes = fwrite (&header, sizeof (header), 1, outfile);
        if (bytes == 1)
            bytes = fwrite (&imageHeader, sizeof (imageHeader), 1, outfile);

        // 
        if (bytes != 1 || pixels.isNull()) {
            fclose (outfile);
            return false;
        }

        // Write each row
        bool status = true;
        long bytesPerRow = imageHeader.width * imageHeader.channels * imageHeader.depth/8;
        for (long y=0; y<imageHeader.height; y++) {
            bytes = fwrite (pixels._rowAddress (y+imageHeader.y0), bytesPerRow, 1, outfile);
            if (bytes != 1) {
                // We didn't read the information
                status = false;
                break;
            }
        }

        fclose (outfile);
        return status;
    }



    // Add this delegate to our list during static initialization
    IAP_installer IAP_installer::installer;
    IAP_installer::IAP_installer ()
    {
        ImageIODelegateList::gOnly().setDelegate ("iap", &IAP::gOnly());
    }
}